﻿
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Red.Core;

namespace Red.Controls.ViewModels
{
    /// <summary>
    ///     Build mode for LazyHierarchicalItem
    /// </summary>
    public enum BuildMode
    {
        /// <summary>
        ///     Build only visible items and their children
        /// </summary>
        Lazy,
        /// <summary>
        ///     Build all items
        /// </summary>
        Full
    };

    /// <summary>
    ///     Base class for all ViewModel classes displayed by hierarchical containers.
    /// </summary>
    /// <typeparam name="T">
    ///     Allows to constrain the enclosed hierarchy (children and parent) to some specific sub-type, saving a lot of casting later
    /// </typeparam>
    public class LazyHierarchicalItem<T> : ViewModel 
        where T : LazyHierarchicalItem<T>
    {
        #region Data

        private readonly ObservableCollection<T> _children = new ObservableCollection<T>();
        private readonly T _parent;

        private bool _isExpanded;
        private bool _isSelected;
        private bool _populated = false;

        #endregion Data

        #region C-tor

        protected LazyHierarchicalItem( T parent )
        {
            _parent = parent;
        }

        #endregion C-tor

        #region Properties

        public T Parent
        {
            get { return _parent; }
        }

        /// <summary>
        /// Returns the logical child items of this object.
        /// </summary>
        public ObservableCollection<T> Children
        {
            get { return _children; }
        }

        /// <summary>
        /// Gets/sets whether the container item
        /// associated with this object is expanded.
        /// </summary>
        public bool IsExpanded
        {
            get { return _isExpanded; }

            set 
            {
                if ( value != _isExpanded ) 
                {
                    _isExpanded = value;
                    OnPropertyChanged();

                    if ( _isExpanded ) 
                    {
                        if ( _parent != null ) 
                        {
                            _parent.IsExpanded = true; // Expand all the way up to the root.
                        }
                        Build( BuildMode.Lazy );
                    }
                }
            }
        }

        /// <summary>
        /// Gets/sets whether the container item 
        /// associated with this object is selected.
        /// </summary>
        public bool IsSelected
        {
            get { return _isSelected; }

            set 
            {
                if ( value != _isSelected ) 
                {
                    _isSelected = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion Properties

        #region Public methods

        /// <summary>
        /// Clears and re-populates item's children (and grand-children for a proper set-up of expand marks)
        /// </summary>
        public void Rebuild( BuildMode mode = BuildMode.Lazy )
        {
            Invalidate();
            Build( mode );
        }

        public void Build( BuildMode mode )
        {
            if ( ( _parent == null || _parent.IsExpanded ) || mode == BuildMode.Full ) 
            {
                if ( !_populated ) 
                {
                    EnumerateChildren( _children );
                    _populated = true;
                }

                foreach ( var obj in _children ) 
                {
                    obj.Build( mode );
                }
            }
        }

        #endregion Public methods

        #region Private methods

        private void Invalidate()
        {
            _populated = false;
            foreach ( var obj in _children ) 
            {
                obj.Invalidate();
            }
        }

        #endregion Private methods

        #region Interface

        /// <summary>
        ///     Invoked when the item needs to update it's children list (like expanding or after calling Refresh).
        /// </summary>
        /// <param name="children">
        ///     Current list of children to be modified to reflect the current item's state.
        /// </param>
        protected virtual void EnumerateChildren( ICollection<T> children )
        {
        }

        #endregion Interface

    }

    /// <summary>
    /// Base class for all ViewModel classes displayed by hierarchical containers (like TreeViewItems). 
    /// Universal "unconstrained" version (children and parent are LazyHierarchicalItems)
    /// </summary>
    public class LazyHierarchicalItem : LazyHierarchicalItem<LazyHierarchicalItem> 
    {
        protected LazyHierarchicalItem( LazyHierarchicalItem parent )
            : base( parent )
        {
        }
    }
}